Managing Configuration Changes and Screen State Restoration
Course: Android App Development
Topic: Managing Configuration Changes and Screen State Restoration
Course Link: Android App Development Course
Demo Link: Register for a Free Demo
Website: JustAcademy Official Website
1. Introduction
Android applications run on devices with different screen sizes, orientations, languages, themes, and hardware configurations. During the application's lifetime, these configurations can change because the user rotates the device, switches to dark mode, resizes a window, changes the system language, or connects an external keyboard.
Some configuration changes cause Android to destroy and recreate the current Activity. When this happens, values stored only in the Activity's instance variables can be lost. For example, a user may be filling out a registration form, writing a message, or searching for a product when the screen rotates. Without proper state management, the entered information may disappear.
Configuration change management means ensuring that the application adapts correctly to a changed device configuration. Screen state restoration means recovering the relevant UI state after an Activity, Fragment, or application process is recreated.
2. Learning Objectives
- Understand Android configuration changes and Activity recreation.
- Identify different types of UI state and application data.
- Understand Activity lifecycle callbacks during configuration changes.
- Use onSaveInstanceState() and saved instance state Bundles.
- Preserve screen state using ViewModel and SavedStateHandle.
- Restore user input and UI state in Jetpack Compose.
- Distinguish configuration changes from process death.
- Implement reliable state restoration using Kotlin.
- Test state restoration and troubleshoot common problems.
3. What Are Configuration Changes?
A configuration change occurs when an environmental property affecting the application changes while it is running. Android provides configuration information through resources and system APIs so that the application can adjust its UI and behavior accordingly.
Common configuration changes
| Configuration Change |
Example |
Possible Effect |
| Orientation |
Portrait changes to landscape |
Activity recreation and layout changes |
| Screen size |
App window is resized |
Different layout arrangements may be required |
| Window size class |
Foldable device is unfolded |
UI may need to display additional content |
| Dark or light theme |
User changes system theme |
Colors and theme resources may change |
| Language or locale |
User changes the device language |
Localized text and formatting may change |
| Font scale |
User increases text size |
Text wrapping and layout dimensions may change |
| Keyboard availability |
Physical keyboard connects |
Input and layout behavior may change |
| Display density |
App moves to a display with different density |
Resources and dimensions may change |
Android does not necessarily recreate an Activity for every environmental change. The exact behavior depends on the change, the Android version, the app's configuration, and the APIs involved. Applications should be designed to handle relevant changes without losing important state.
4. Why Does Android Recreate an Activity?
Android uses Activity recreation to reload resources and allow an application to rebuild its interface for the new configuration. For example, an application may provide separate layouts for portrait and landscape orientation.
When the Activity is recreated, Android creates a new Activity instance. The old instance's ordinary instance variables do not automatically transfer to the new instance. Therefore, state must be retained or restored through an appropriate mechanism.
Example of state loss
class MainActivity : AppCompatActivity() {
private var userName = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
In this example, userName is stored in the Activity instance. If the Activity is destroyed and recreated, the new instance initializes userName to an empty string. Unless the value is restored from another source, the previous value is lost.
5. Understanding Activity Lifecycle During Recreation
Activity lifecycle callbacks help developers understand when the current screen is created, stopped, destroyed, and recreated.
Typical sequence
- onPause(): The Activity loses its fully active state.
- onSaveInstanceState(): Android may request a saved instance state snapshot before the Activity is destroyed.
- onStop(): The Activity is no longer visible.
- onDestroy(): The old Activity instance is destroyed.
- onCreate(): A new Activity instance is created, potentially receiving the saved Bundle.
- onStart(): The new Activity becomes visible.
- onResume(): The Activity returns to the foreground and becomes interactive.
This is a typical sequence, not a guarantee that every callback occurs in precisely this order for every lifecycle scenario. Saved-state timing and callbacks depend on the situation. State-saving code should not depend on receiving a callback that Android does not guarantee.
6. Types of State in Android Applications
Choosing the right restoration strategy starts with identifying what kind of information needs to survive.
6.1 UI State
UI state describes the current appearance and interaction state of a screen.
- Text entered in a search field.
- Selected tab or filter.
- Current scroll position.
- Expanded or collapsed sections.
- Selected item in a list.
- Current navigation destination.
6.2 Screen or Business State
This state represents information required by the screen's logic, such as a selected product ID, a search query, loading status, or the data needed to rebuild a result screen.
6.3 Persistent Application Data
This includes information that must remain available beyond Activity recreation or process death, such as saved notes, user preferences, offline records, and completed transactions. Databases, DataStore, or appropriate server-side storage are generally better suited for durable data.
7. Methods for Managing Configuration Changes
| Method |
Primary Purpose |
Survives Configuration Change |
Survives System Process Death |
| ViewModel |
Retain in-memory screen data |
Yes |
No, not by itself |
| onSaveInstanceState() |
Save a small state snapshot |
Yes |
Usually, when Android saved and restores the state |
| SavedStateHandle |
Retain small ViewModel state for restoration |
Yes |
Supports system restoration |
| rememberSaveable |
Save small Compose UI state |
Yes |
Supports system restoration |
| Room database |
Persist structured application data |
Yes |
Yes |
| DataStore |
Persist preferences and small settings |
Yes |
Yes |
Saved-state mechanisms have size and serialization limitations. They are intended for small amounts of information needed to reconstruct a screen, not large bitmaps, large result lists, or complete database records.
8. Using onSaveInstanceState() to Save Screen State
The onSaveInstanceState() callback allows an Activity to place a small amount of state into a Bundle. Android can supply that Bundle to the recreated Activity through onCreate() or onRestoreInstanceState().
Example: Saving a user's name
class MainActivity : AppCompatActivity() {
private var userName: String = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
userName = savedInstanceState?.getString("USER_NAME") ?: ""
val nameInput = findViewById<EditText>(R.id.nameInput)
nameInput.setText(userName)
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putString("USER_NAME", userName)
super.onSaveInstanceState(outState)
}
}
In this example, the application saves the name under the USER_NAME key and retrieves it when the Activity is recreated. In a real form, update userName whenever the user changes the text, or read the current text from the EditText when saving.
Reading the value directly from the input field
override fun onSaveInstanceState(outState: Bundle) {
val nameInput = findViewById<EditText>(R.id.nameInput)
outState.putString(
"USER_NAME",
nameInput.text.toString()
)
super.onSaveInstanceState(outState)
}
Important points
- Use stable keys to identify saved values.
- Restore values before or while binding the UI.
- Save only small, necessary values.
- Do not assume saved state is always available; the Bundle may be null on a fresh launch.
- Do not rely on onSaveInstanceState() as a guaranteed final save for every way an app can terminate.
9. Restoring State with onRestoreInstanceState()
Android may invoke onRestoreInstanceState() after onStart() when saved state is available. It provides another place to restore saved values after the Activity has been created.
class ProfileActivity : AppCompatActivity() {
private var selectedTab = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_profile)
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putInt("SELECTED_TAB", selectedTab)
super.onSaveInstanceState(outState)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
selectedTab = savedInstanceState.getInt("SELECTED_TAB", 0)
// Update the selected tab in the UI here.
}
}
Use either onCreate() or onRestoreInstanceState() according to your initialization needs. Restoring in onCreate() is useful when the state must be available during initial UI setup. The Bundle can be null in onCreate() when no saved state exists.
10. Managing State with ViewModel
A ViewModel stores UI-related data separately from an Activity or Fragment. The same ViewModel instance normally survives configuration-driven Activity recreation, allowing screen data to remain available without being saved and reloaded for every rotation.
A ViewModel does not, by itself, survive system-initiated process death. For that scenario, combine it with SavedStateHandle or load durable information from persistent storage.
Example: ViewModel for a search screen
class SearchViewModel : ViewModel() {
var searchQuery: String = ""
private set
fun updateQuery(query: String) {
searchQuery = query
}
}
An Activity can obtain the ViewModel through the AndroidX ViewModel APIs:
class SearchActivity : AppCompatActivity() {
private val viewModel: SearchViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)
val searchInput = findViewById<EditText>(R.id.searchInput)
searchInput.setText(viewModel.searchQuery)
searchInput.doAfterTextChanged { text ->
viewModel.updateQuery(text?.toString().orEmpty())
}
}
}
This example uses the AndroidX KTX extension doAfterTextChanged, which requires the appropriate AndroidX dependency. The ViewModel preserves the query through ordinary configuration changes. Use SavedStateHandle if the query must also be restored after system-initiated process death.
11. Using SavedStateHandle in a ViewModel
SavedStateHandle provides access to saved state inside a ViewModel. It is useful when screen state participates in business logic and must be restored after the system recreates the process and screen.
Example
class SearchViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
companion object {
private const val KEY_QUERY = "search_query"
}
val searchQuery: String
get() = savedStateHandle[KEY_QUERY] ?: ""
fun updateQuery(query: String) {
savedStateHandle[KEY_QUERY] = query
}
}
When using a standard ViewModel factory or supported AndroidX integration, SavedStateHandle can be provided automatically. If creating ViewModels manually, configure the required factory and saved-state owner correctly.
Why SavedStateHandle is useful
- It keeps small screen state near the ViewModel that uses it.
- It survives normal configuration changes through the ViewModel.
- It can restore saved values after system-initiated process recreation.
- It helps rebuild a screen using a query, selected item ID, or navigation argument.
12. Handling Configuration Changes in Jetpack Compose
Jetpack Compose uses state-driven UI. When a composable reads state and that state changes, Compose can recompose the relevant UI. However, ordinary remember state is not designed to survive every Activity recreation or process death.
Using rememberSaveable
@Composable
fun NameScreen() {
var name by rememberSaveable {
mutableStateOf("")
}
Column {
OutlinedTextField(
value = name,
onValueChange = { name = it },
label = { Text("Enter your name") }
)
Text(text = "Hello, $name")
}
}
rememberSaveable preserves supported small UI state through configuration changes and can participate in system-initiated state restoration. For custom objects, use an appropriate Saver or a supported serialization mechanism. Do not put large objects into saved UI state.
remember and rememberSaveable comparison
| Feature |
remember |
rememberSaveable |
| Retains state during recomposition |
Yes |
Yes |
| Designed to survive Activity recreation |
No |
Yes |
| Can restore small saved state after system process death |
No |
Yes, when restoration state is available |
| Best use |
Temporary composition state |
Small user-facing UI state |
13. Handling Configuration Changes with View Binding
In View-based applications, the layout can be recreated when configuration changes occur. Views from the old Activity instance must not be reused in the new instance.
- Inflate or bind the new layout in the recreated Activity.
- Keep reusable screen data in a ViewModel.
- Restore lightweight transient state using saved-state APIs.
- Re-observe LiveData or Flow using lifecycle-aware collection.
- Release references to old Views when the lifecycle owner is destroyed.
Fragments and their view lifecycles also require care. A Fragment can remain alive while its view is destroyed and recreated. Observe view-related data with viewLifecycleOwner and clear view binding references in onDestroyView() when appropriate.
14. Adapting the UI to New Configurations
Preserving state is only one part of configuration handling. The application must also present an appropriate interface for the new environment.
Recommended practices
- Use resource-qualified layouts and values where appropriate, such as layout-land or values-night.
- Prefer responsive layouts over hardcoded screen dimensions.
- Use dp for layout dimensions and sp for text sizes in View-based layouts.
- Support window resizing and multi-window operation.
- Use accessible layouts that accommodate larger fonts and different languages.
- Ensure theme changes update colors, icons, and other themed resources correctly.
- Keep business logic independent of a particular Activity instance.
15. Should You Override Configuration Handling?
Android provides mechanisms such as android:configChanges for selected configuration changes. These can prevent an Activity from being recreated for specified changes, allowing the Activity to receive onConfigurationChanged() instead.
This is not the default recommendation for ordinary applications. Overriding the standard recreation behavior can make it harder to refresh resources and correctly adapt the UI. Use it only when there is a clear technical reason and the application explicitly handles every relevant configuration update.
For most applications, the better approach is to support Activity recreation correctly, retain screen data in a ViewModel, restore small transient values through saved state, and reload persistent data from its source.
16. Configuration Changes vs. Process Death
| Aspect |
Configuration Change |
System-Initiated Process Death |
| Example |
Rotating the screen |
Android terminates a background process to reclaim resources |
| Activity instance |
Usually recreated for relevant changes |
Recreated when the user returns to the app |
| ViewModel |
Normally retained for the same logical owner |
Destroyed with the process |
| Saved state |
Can restore transient UI values |
Can restore values if Android previously captured and supplies saved state |
| Database or durable storage |
Remains available |
Remains available |
Saved instance state is not a substitute for persistent storage. It is intended to restore the user's immediate context when Android recreates a screen. Data that must remain safe across app restarts, explicit dismissal, or device restarts should be persisted appropriately.
17. Complete Example: Restoring a Search Screen
The following example combines SavedStateHandle with a simple search screen. It demonstrates how to retain the search query and rebuild the screen after recreation. In a production app, the ViewModel would typically expose observable state and perform repository-backed searches.
ViewModel
class ProductSearchViewModel(
private val state: SavedStateHandle
) : ViewModel() {
companion object {
private const val QUERY_KEY = "product_query"
}
val query: String
get() = state[QUERY_KEY] ?: ""
fun onQueryChanged(value: String) {
state[QUERY_KEY] = value
}
}
Activity
class ProductSearchActivity : AppCompatActivity() {
private val viewModel: ProductSearchViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_product_search)
val searchInput =
findViewById<EditText>(R.id.searchInput)
searchInput.setText(viewModel.query)
searchInput.doAfterTextChanged { editable ->
viewModel.onQueryChanged(
editable?.toString().orEmpty()
)
}
}
}
How the example works
- The user types a product search query.
- The text listener sends the latest value to the ViewModel.
- The ViewModel stores the query in SavedStateHandle.
- During an ordinary configuration change, the ViewModel remains available.
- If Android later restores the Activity after system-initiated process death, the saved query can be restored when saved state is available.
- The Activity repopulates the input field using the stored query.
Note: This example restores the query, not the search results themselves. The application should execute the search again or load the appropriate cached or persistent data. Avoid triggering unnecessary duplicate requests when restoring the screen.
18. Common Problems and Troubleshooting
| Problem |
Possible Cause |
Solution |
| Text disappears after rotation |
Text exists only in a normal Activity variable |
Use ViewModel, saved state, or supported view-state restoration |
| Compose text resets |
State uses remember instead of rememberSaveable |
Use rememberSaveable for small restorable UI values |
| ViewModel data disappears after process death |
ViewModel only retains in-memory data |
Use SavedStateHandle and/or persistent storage |
| Search executes repeatedly |
Search logic runs unconditionally during UI creation |
Separate state restoration from search execution and use appropriate Flow or lifecycle patterns |
| Old Views cause crashes |
References point to a destroyed Activity or Fragment view |
Rebind views and clear obsolete references |
| Large saved Bundle causes delays |
Complex objects or large data are serialized |
Save IDs or small values and reload large data from storage |
| Dark mode does not update correctly |
UI assumes the old configuration remains valid |
Use theme-aware resources and support configuration recreation |
| Restoration returns a default value |
Wrong Bundle key or missing state initialization |
Check key consistency and distinguish a fresh launch from restored state |
19. Best Practices
- Separate UI from business logic: Avoid storing important application state only in Activity fields.
- Use ViewModel for active screen state: Retain data across normal configuration changes.
- Use saved-state APIs for lightweight state: Store queries, IDs, selections, and other small values needed to rebuild the screen.
- Persist durable data: Use Room, DataStore, files, or an appropriate backend for information that must survive beyond the current screen.
- Restore the minimum required state: Reconstruct complex objects from stable identifiers instead of serializing entire objects.
- Make UI rendering repeatable: Recreating a screen should not accidentally submit forms, duplicate payments, or repeat other one-time actions.
- Handle asynchronous work safely: Use lifecycle-aware observation and cancellation where appropriate.
- Test multiple configurations: Include rotation, resizing, font scaling, theme changes, and language changes.
- Respect system behavior: Do not disable configuration recreation merely to avoid implementing state restoration.
- Protect sensitive data: Do not place passwords, authentication secrets, or unnecessary personal information in saved UI state.
20. Practical Exercises
Exercise 1: Restore a Registration Form
Create a registration screen with name, email, and phone number fields. Rotate the device and ensure the user's unfinished input is preserved. Use appropriate saved-state mechanisms and avoid persisting unnecessary sensitive information.
Exercise 2: Build a Search Screen
Create a search screen using a ViewModel and SavedStateHandle. Preserve the search query and restore the screen after configuration changes. Ensure that results can be reloaded without unnecessary duplicate network requests.
Exercise 3: Compose State Restoration
Create a Jetpack Compose screen containing a text field, selected filter, and expanded section. Use rememberSaveable for the small state values and test the screen after rotating the device.
Exercise 4: Responsive Layout
Create portrait and landscape layouts, or use a responsive layout that adapts to the available width. Verify that the same screen data remains available after the layout changes.
Mini Project: Restorable Notes Application
- Create a screen to write and edit notes.
- Use a ViewModel to manage the currently edited note.
- Use saved state to retain the current note ID and editing context.
- Use Room or another persistent store to save actual note content.
- Restore the editor after rotation and system-initiated process recreation.
- Test that saved notes remain available after closing and reopening the application.
21. Interview Questions and Answers
Q1. What is a configuration change in Android?
Answer: It is a change in device or application environment, such as orientation, screen size, theme, or locale, that may require the application to update its resources or recreate its Activity.
Q2. Why does Android recreate an Activity?
Answer: Android may recreate the Activity to apply resources and configuration appropriate for the new environment. This also gives the application an opportunity to rebuild its UI for the changed configuration.
Q3. What is onSaveInstanceState() used for?
Answer: It saves a small amount of transient UI state into a Bundle so Android can restore that state if it recreates the Activity.
Q4. What is the difference between ViewModel and SavedStateHandle?
Answer: ViewModel retains screen data in memory across configuration changes. SavedStateHandle stores small state values that can be restored after system-initiated process recreation when saved state is available.
Q5. Does a ViewModel survive process death?
Answer: No. The ViewModel is destroyed with the application process. Use SavedStateHandle for small restorable state and persistent storage for durable application data.
Q6. What is the purpose of rememberSaveable?
Answer: It preserves supported small Compose UI state through recomposition, configuration changes, and eligible system-initiated state restoration.
Q7. Should large objects be stored in a saved-state Bundle?
Answer: No. Save a small identifier or the minimum required information, then reload complex data from a database, repository, or other durable source.
Q8. What is the difference between configuration change and process death?
Answer: A configuration change may recreate an Activity while preserving its ViewModel. Process death terminates the application process and destroys its in-memory objects, so restoration requires saved state or persistent storage.
Q9. Why should developers avoid unnecessarily handling configuration changes manually?
Answer: Manual handling increases complexity and requires the app to update resources, layouts, and other configuration-dependent behavior itself. Default Activity recreation is usually simpler and safer when state is managed properly.
Q10. How can configuration changes be tested?
Answer: Rotate the device, resize the app window, change theme and font scale, change locale, and test system-initiated process recreation. Verify that both UI adaptation and relevant user state are correct.
22. Quick Revision Checklist
- Understand why Android recreates Activities.
- Identify which values are transient UI state and which are durable application data.
- Use ViewModel to retain active screen data across configuration changes.
- Use onSaveInstanceState() for small, restorable Activity state when appropriate.
- Use SavedStateHandle for small ViewModel state that must be restored after system process recreation.
- Use rememberSaveable for supported Compose UI state.
- Store durable application data in persistent storage.
- Adapt layouts and resources to the new configuration.
- Test rotation, resizing, process death, and other relevant changes.
23. Conclusion
Managing configuration changes and screen state restoration is essential for building reliable Android applications. Android may recreate Activities when the device configuration changes, and the system may terminate background processes when resources are needed. Applications should therefore separate screen state from Activity instances and choose the correct restoration mechanism for each type of information.
Use ViewModel for in-memory UI data, saved-state APIs for small values needed to reconstruct a screen, and persistent storage for durable application data. In Jetpack Compose, use rememberSaveable for suitable UI state. With these practices, users can rotate their devices, resize application windows, and return to interrupted screens without unnecessarily losing their progress.
Additional Learning Resources
Explore Android App Development with JustAcademy
Build your Android development skills through structured learning and practical projects.
Explore the Android App Development Course
Register for a Course Demo
Visit JustAcademy